package com.scalar.chiptrack.finance.reports.actions;

import com.scalar.chiptrack.ChipTrackAction;
import com.scalar.chiptrack.utils.DateUtil;
import com.scalar.chiptrack.utils.ExcelUtils;
import com.scalar.chiptrack.sales.dao.NewCustomerDAO;
import com.scalar.chiptrack.operations.process.dao.NewProductDAO;
import com.scalar.chiptrack.finance.reports.dao.DirectMarginDAO;
import com.scalar.chiptrack.finance.reports.forms.DirectMarginReportForm;
import com.scalar.chiptrack.finance.reports.DirectMargin;
import com.scalar.chiptrack.finance.reports.DirectMarginComparator;
import com.scalar.chiptrack.finance.dao.CostCodeSetupDAO;
import com.scalar.chiptrack.commons.SessionKeys;
import com.scalar.chiptrack.commons.Constants;
import com.scalar.chiptrack.users.User;
import com.scalar.ScalarException;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import org.apache.struts.util.LabelValueBean;
import org.apache.struts.util.ResponseUtils;
import org.apache.commons.beanutils.BeanUtils;

import org.apache.poi.ss.usermodel.Workbook;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Collections;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * User: prasad
 * Date: Jun 11, 2007
 * Time: 4:10:52 PM
 */

public class DirectMarginReportAction extends ChipTrackAction
{
    private static final String ACTION_NAME = "/directMarginReport.do";

    public ActionForward execute( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response ) throws Exception
    {

        String action           = request.getParameter( "action" );
        if ( action == null )
        {
            action = "directMarginReportSearch";
        }
        HttpSession session     = request.getSession();
        HashMap errors 	        = new HashMap();
        HashMap info 	        = new HashMap();

        ActionForward actionForward         = mapping.findForward( "success" );
        ArrayList directMarginList          = new ArrayList();
        DirectMarginReportForm reportForm   = ( DirectMarginReportForm ) form;

        request.setAttribute ( SessionKeys.ERROR_MESSAGES, errors );
        request.setAttribute ( SessionKeys.INFO_MESSAGES, info );

        User user = ( User ) session.getAttribute( SessionKeys.USER_KEY );

        if( user == null && !action.startsWith( "ajax" ) ) //avoiding checking session for ajax request. need to handle seperately
        {
            session.setAttribute( SessionKeys.SOURCE_PAGE_KEY, ACTION_NAME );
            return ( mapping.findForward( "login" ) );
        }

        if( "directMarginReportSearch".equalsIgnoreCase( action ) )
        {

            try
            {
                reportForm.setSearchStatus( "Shipments" );
                reportForm.setSearchDateType( "booking" );
                reportForm.setSearchGroupBy( "customer" );
                reportForm.setSearchSortBy( "value" );
                reportForm.setSearchOrderBy( "ascending" );

                request.setAttribute( "statusList", loadStatuses() );
                request.setAttribute( "costCodeList", CostCodeSetupDAO.loadCostCodes( null )  );
                request.setAttribute( "customerList", NewCustomerDAO.getAllCustomerNamesByCustomerType( "Customer" )  );
                request.setAttribute( "partNumberList", NewProductDAO.loadMarketingPartNumbers( null, Constants.LOCATION_TYPE_FG ) );
            }
            catch( ScalarException e )
            {
                e.printStackTrace();
                errors.put ( "Could not Load Direct Margin Search Information", null );
            }

            actionForward = mapping.findForward( "directMarginReportSearch" );
            
            return ( actionForward );
        }
        else
        {
            actionForward = mapping.findForward( "directMarginReport" );
            //System.out.println("----------DirectMarginReportAction.execute  - Customer-Id ------------- = " + reportForm.getCustomerId() + " ProcessstepId = " + reportForm.getProcessstepId() + " costcode = " + reportForm.getCostCode() );
            ArrayList directMarginsGrouped = null;
            ArrayList directMargins = null;
            try
            {
                directMargins = DirectMarginDAO.loadDirectMargins( null, reportForm );
                if( directMargins != null )
                {
                    directMarginsGrouped = getDirectMarginsGroupBy ( directMargins, reportForm.getSearchGroupBy(), 1);
                    Collections.sort( directMarginsGrouped, new DirectMarginComparator( reportForm.getSearchSortBy(), reportForm.getSearchGroupBy(), reportForm.getSearchOrderBy() ));
                }
            }
            catch ( ScalarException e )
            {
                e.printStackTrace();
                errors.put ( "Could not Load Direct Margin Search Information", null );
            }

            
            if ( "loadDirectMargins".equalsIgnoreCase( action ) )
            {
                if ( directMarginsGrouped != null && directMarginsGrouped.isEmpty() )
                {
                    info.put( "Could not find Direct Margins for the given search criteria", null );
                }
                else
                {
                    reportForm.setDirectMarginList( directMarginsGrouped );
                }
            }
            else if ( "ajaxLoadDirectMargins".equalsIgnoreCase( action ) )
            {
                String dmXML;
                if ( user != null )
                {
                    dmXML = getDirectMarginXML( directMarginsGrouped, reportForm.getSearchGroupBy() );
                }
                else //Session Expired..need to notify the user
                {
                    dmXML = "<dms><exception>Session has been time out, Your request could not be processed. Please login again.</exception></dms>";
                }
                // Write XML to response.
                response.setContentType("application/xml");
                response.getWriter().write( dmXML );
                return null;
            }
            else if ( "exportDirectMargins".equalsIgnoreCase( action ) )
            {
                ArrayList finalList = null;
                ArrayList level1 = null;
                ArrayList level2 = null;
                ArrayList level3 = null;

                if ( "customer".equalsIgnoreCase( reportForm.getSearchGroupBy() ) )
                {
                    level1 = directMarginsGrouped;
                    level2 =  getDirectMarginsGroupBy ( directMargins, "customerProduct", 2);
                    level3 =  getDirectMarginsGroupBy ( directMargins, "customerProductSO", 3);
                }
                if ( "product".equalsIgnoreCase( reportForm.getSearchGroupBy() ) )
                {
                    level1 = directMarginsGrouped;
                    level2 =  getDirectMarginsGroupBy ( directMargins, "customerProduct", 2);
                    level3 =  getDirectMarginsGroupBy ( directMargins, "customerProductSO", 3);
                }
                if ( "costCode".equalsIgnoreCase( reportForm.getSearchGroupBy() ) )
                {
                    level1 = directMarginsGrouped;
                    level2 =  getDirectMarginsGroupBy ( directMargins, "costCodeProduct", 2);
                    level3 =  getDirectMarginsGroupBy ( directMargins, "costCodeProductCustomer", 3);
                }
                finalList = mergeLevels(level1, level2, level3, reportForm.getSearchSortBy(), reportForm.getSearchGroupBy(), reportForm.getSearchOrderBy() );
                Workbook workbook = DirectMarginDAO.getWorkbookForDirectMargin( finalList, reportForm.getSearchGroupBy() );
                String filename="directMarginReport_"+ DateUtil.getTimeStamp();

                ExcelUtils.generateExcelReport( workbook, filename, response );
            }
        }

        return ( actionForward );
    }

    public static ArrayList loadStatuses()
    {
        ArrayList statuses = new ArrayList();

        statuses.add( new LabelValueBean( "Backlog", "Backlog" ) );
        statuses.add( new LabelValueBean( "Backlog + Shipment", "Backlog_Shipment" ) );
        statuses.add( new LabelValueBean( "Shipment", "Shipment" ) );
        statuses.add( new LabelValueBean( "Booking", "Booking" ) );

        return statuses;
    }
    private ArrayList getDirectMarginsGroupBy(ArrayList directMargins, String groupBy, int level) throws Exception
    {
        HashMap map = new HashMap();
        DirectMargin directMargin = null;
        DirectMargin directMarginGrouped = null;
        String groupByKey = null;
        int size = (directMargins != null ? directMargins.size() : 0 );
        for ( int i = 0; i < size; i++ )
        {
            directMargin = (DirectMargin)directMargins.get(i);
            if ( "customer".equalsIgnoreCase( groupBy ) )
            {
                groupByKey = directMargin.getCustomerName();
            }
            else if ( "product".equalsIgnoreCase( groupBy ) )
            {
                groupByKey = directMargin.getPartNumber();
            }
            else if ( "costCode".equalsIgnoreCase( groupBy ) )
            {
                groupByKey = directMargin.getCostCode();
            }
            else if ( "salesOrder".equalsIgnoreCase( groupBy ) )
            {
                groupByKey = directMargin.getSalesOrderNo();
            }
            else if ( "customerProduct".equalsIgnoreCase( groupBy ) )
            {
                groupByKey = directMargin.getCustomerName() + "#" + directMargin.getPartNumber();
            }
            else if ( "customerProductSO".equalsIgnoreCase( groupBy ) )
            {
                groupByKey = directMargin.getCustomerName() + "#" + directMargin.getPartNumber() + "#" +directMargin.getSalesOrderNo();
            }
            else if ( "costCodeProduct".equalsIgnoreCase( groupBy ) )
            {
                groupByKey = directMargin.getCostCode() + "#" + directMargin.getPartNumber();
            }
            else if ( "costCodeProductCustomer".equalsIgnoreCase( groupBy ) )
            {
                groupByKey = directMargin.getCostCode() + "#" + directMargin.getPartNumber() + "#" +directMargin.getCustomerName();
            }
            directMarginGrouped = (DirectMargin)map.get ( groupByKey );
            if ( directMarginGrouped == null )
            {
                directMarginGrouped = (DirectMargin)directMargin.clone();
                directMarginGrouped.setCount(1);
                directMarginGrouped.setLevel( level );
                map.put(groupByKey, directMarginGrouped);
            }
            else
            {
                directMarginGrouped.setUnitsCost( ( ( directMarginGrouped.getUnitsCost() * directMarginGrouped.getUnits() + directMargin.getUnitsCost() * directMargin.getUnits() ) / ( directMarginGrouped.getUnits() + directMargin.getUnits() ) ) ); //total AVG UNIT COST
                directMarginGrouped.setDirectMargin( ( ( directMarginGrouped.getDirectMargin() * directMarginGrouped.getUnits() + directMargin.getDirectMargin() * directMargin.getUnits() ) / ( directMarginGrouped.getUnits() + directMargin.getUnits() ) ) ); //total AVG DIFF_COST
                directMarginGrouped.setDirectMarginPerctage( ( ( directMarginGrouped.getDirectMarginPerctage() * directMarginGrouped.getUnits() + directMargin.getDirectMarginPerctage() * directMargin.getUnits() ) / ( directMarginGrouped.getUnits() + directMargin.getUnits() ) ) ); //total AVG DIFF_COST_PERCENTAGE
                directMarginGrouped.setUnits( directMarginGrouped.getUnits() + directMargin.getUnits() ); //total coolQty
                directMarginGrouped.setUnitsPrice( directMarginGrouped.getUnitsPrice()+directMargin.getUnitsPrice() );
                directMarginGrouped.setActualCost( ((directMarginGrouped.getActualCost()*directMarginGrouped.getCount())+directMargin.getActualCost())/(directMarginGrouped.getCount()+1) ); //total AVG COST_CODE_COST
                directMarginGrouped.setCount( directMarginGrouped.getCount()+1 );
            }
        }
        return new ArrayList( map.values() );
    }
    private String getDirectMarginXML ( ArrayList directMargins, String groupBy )
    {
        StringBuffer directMarginXML = new StringBuffer();
        directMarginXML.append ( "<dms><exception/><grpby>" ).append( groupBy ).append( "</grpby>" );
        
        int liseSize = (directMargins == null ) ? 0 : directMargins.size();
        for ( int i=0; i < liseSize; i++ )
        {
            DirectMargin directMargin =  ( DirectMargin ) directMargins.get( i );
            directMarginXML.append ( "<dm>" );
            directMarginXML.append ( "<cust id='" ).append( directMargin.getCustomerId() ).append( "'>").append ( ResponseUtils.filter( directMargin.getCustomerName() ) ).append( "</cust>");
            directMarginXML.append ( "<pn id='" ).append( directMargin.getProcessstepId() ).append( "'>").append ( ResponseUtils.filter( directMargin.getPartNumber() ) ).append( "</pn>");
            directMarginXML.append ( "<cc>" ).append ( ResponseUtils.filter( directMargin.getCostCode() ) ).append ( "</cc>");
            directMarginXML.append ( "<so>" ).append ( ResponseUtils.filter( directMargin.getSalesOrderNo() ) ).append ( "</so>");
            directMarginXML.append ( "<ac>" ).append ( directMargin.getActualCostFormatted() ).append ( "</ac>");
            directMarginXML.append ( "<units>" ).append ( directMargin.getUnitsFormatted() ).append ( "</units>");
            directMarginXML.append ( "<ucost>" ).append ( directMargin.getUnitsCostFormatted() ).append ( "</ucost>");
            directMarginXML.append ( "<amount>" ).append ( directMargin.getUnitsPriceFormatted() ).append ( "</amount>");            
            directMarginXML.append ( "<dmargin>" ).append ( directMargin.getDirectMarginFormatted() ).append ( "</dmargin>");
            directMarginXML.append ( "<dmpcent>" ).append ( directMargin.getDirectMarginPerctageFormatted() ).append ( "</dmpcent>");
            directMarginXML.append( "</dm>" );
        }
        directMarginXML.append ( "</dms>" );
        return directMarginXML.toString();
    }

    private ArrayList mergeLevels ( ArrayList level1, ArrayList level2, ArrayList level3, String sortBy, String baseGroupBy, String orderBy ) throws Exception
    {
        ArrayList mergedList = new ArrayList();
        Collections.sort( level1, new DirectMarginComparator( sortBy, baseGroupBy, orderBy ) );
        int level1Size = level1.size();
        for ( int i=0; i < level1Size; i++ )
        {
            DirectMargin dmlevel1 = (DirectMargin)level1.get(i);
            mergedList.add ( dmlevel1 );
            ArrayList level2ForLevel1 = null;
            if( "customer".equalsIgnoreCase( baseGroupBy ) )
            {
                level2ForLevel1 = filterDirectMargins( level2, "customerName", null, dmlevel1.getCustomerName(), null );
            }
            else if ( "product".equalsIgnoreCase( baseGroupBy ) )
            {
                level2ForLevel1 = filterDirectMargins( level2, "partNumber", null, dmlevel1.getPartNumber(), null );
            }
            else if ( "costCode".equalsIgnoreCase( baseGroupBy ) )
            {
                level2ForLevel1 = filterDirectMargins( level2, "costCode", null, dmlevel1.getCostCode(), null );
            }

            Collections.sort( level2ForLevel1, new DirectMarginComparator( sortBy, "product", orderBy ) );
            int level2Size = level2ForLevel1.size();
            for ( int j=0; j < level2Size; j++ )
            {
                DirectMargin dmlevel2 = (DirectMargin)level2ForLevel1.get(j);
                mergedList.add ( dmlevel2 );
                ArrayList level3ForLevel1 = null;
                if( "customer".equalsIgnoreCase( baseGroupBy ) )
                {
                    level3ForLevel1 = filterDirectMargins( level3, "customerName", "partNumber", dmlevel2.getCustomerName(), dmlevel2.getPartNumber() );
                    Collections.sort( level3ForLevel1, new DirectMarginComparator( sortBy, "salesOrder", orderBy ) );
                }
                if( "product".equalsIgnoreCase( baseGroupBy ) )
                {
                    level3ForLevel1 = filterDirectMargins( level3, "partNumber", "customerName", dmlevel2.getPartNumber(), dmlevel2.getCustomerName()  );
                    Collections.sort( level3ForLevel1, new DirectMarginComparator( sortBy, "salesOrder", orderBy ) );
                }
                else if ( "costCode".equalsIgnoreCase( baseGroupBy ) )
                {
                    level3ForLevel1 = filterDirectMargins( level3, "costCode", "partNumber", dmlevel2.getCostCode(), dmlevel2.getPartNumber()  );
                    Collections.sort( level3ForLevel1, new DirectMarginComparator( sortBy, "customer", orderBy ) );
                }
                int level3Size = level3ForLevel1.size();

                for ( int k=0; k < level3Size; k++ )
                {
                    DirectMargin dmlevel3 = (DirectMargin)level3ForLevel1.get(k);
                    mergedList.add ( dmlevel3 );
                }
            }
        }
        return mergedList;
    }

    private ArrayList filterDirectMargins( ArrayList list, String property1, String property2, String propertyValue1, String propertyValue2 ) throws Exception
    {
        ArrayList filteredList = new ArrayList();
        int listSize = list.size();
        String finalValue = ( propertyValue1 != null ? propertyValue1 : "" ) + "#" + ( propertyValue2 != null ? propertyValue2 : "" );
        for ( int i = (listSize-1); i >= 0; i-- )
        {
            DirectMargin directMargin = (DirectMargin)list.get( i );
            String value = ( property1 != null ? BeanUtils.getProperty( directMargin, property1 ) : "" ) + "#" + ( property2 != null ? BeanUtils.getProperty( directMargin, property2 ) : "" );
            if ( value.equals( finalValue ) )
            {
                filteredList.add( directMargin );
                list.remove(i);
            }
        }
        return filteredList;
    }
}
